package com.chen.service.impl;

import cn.hutool.core.lang.Snowflake;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.CreateCache;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chen.domain.Account;
import com.chen.domain.CashWithdrawAuditRecord;
import com.chen.domain.CashWithdrawals;
import com.chen.domain.Coin;
import com.chen.dto.AdminUserDto;
import com.chen.dto.UserBankDto;
import com.chen.dto.UserDto;
import com.chen.enums.AccountEnums;
import com.chen.enums.DirectionEnums;
import com.chen.fegin.AdminUserFegin;
import com.chen.fegin.UserBankFegin;
import com.chen.fegin.UserFegin;
import com.chen.mapper.CashWithdrawalsMapper;
import com.chen.service.*;
import com.chen.utils.UserUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class CashWithdrawalsServiceImpl extends ServiceImpl<CashWithdrawalsMapper, CashWithdrawals> implements CashWithdrawalsService{

    @Resource
    private UserFegin userFegin;

    @Resource
    private UserBankFegin userBankFegin;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @CreateCache(name = "CASH_WITHDRAWAL_LOCK:", expire = 100, cacheType = CacheType.BOTH)
    private Cache<String, String> cache;

    private Snowflake snowflake = new Snowflake(3,1);

    @Autowired
    private CashWithdrawalsMapper cashWithdrawalsMapper;

    @Autowired
    private CoinService coinService;

    @Resource
    private AdminUserFegin adminUserFegin;

    @Autowired
    private CashWithdrawAuditRecordService cashWithdrawAuditRecordService;

    @Autowired
    private AccountService accountService;

    @Autowired
    private RecordsService<CashWithdrawals> recordsService;

    @Override
    public Page<CashWithdrawals> findPageForList(Page<CashWithdrawals> page, Long coinId, Long userId, String userName, String mobile, Byte status, String numMax, String numMin, String startTime, String endTime) {
        try {
            RecordsServiceImpl.SearchRecords searchRecords = new RecordsServiceImpl.SearchRecords();

            searchRecords.setCoinId(coinId);
            searchRecords.setUserId(userId);
            searchRecords.setUserName(userName);
            searchRecords.setMobile(mobile);
            searchRecords.setStatus(status);

            Map<RecordsServiceImpl.SearchRecords.BetweenDataEnum, Map<String, RecordsServiceImpl.SearchRecords.LimitRecords>> betweenData = new HashMap<>();

            //设置金钱区间
            Map<String, RecordsServiceImpl.SearchRecords.LimitRecords> limitRecordsMap = new HashMap<>();
            limitRecordsMap.put("num", RecordsServiceImpl.SearchRecords.LimitRecords.builder().min(numMin).max(numMax).build());
            betweenData.put(RecordsServiceImpl.SearchRecords.BetweenDataEnum.AMOUNT, limitRecordsMap);

            //设置金钱区间
            limitRecordsMap = new HashMap<>();
            limitRecordsMap.put("created", RecordsServiceImpl.SearchRecords.LimitRecords.builder().min(startTime).max(endTime).build());
            betweenData.put(RecordsServiceImpl.SearchRecords.BetweenDataEnum.TIME, limitRecordsMap);


            searchRecords.setBetweenData(betweenData);

            return recordsService.findData(page,cashWithdrawalsMapper,searchRecords);
        } catch (InvocationTargetException | NoSuchMethodException | NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void checkWithdrawals(CashWithdrawals cashWithdrawals) {

        //1 当一个员工审核时,另一个员工不能在审核
        //CASH_RECHARGE_LOCK:1231123
        cache.tryLockAndRun(cashWithdrawals.getId() + "", 300, TimeUnit.SECONDS, () -> {
            //审核通过

            if (cashWithdrawals.getStatus() == 1) {
                //修改原用的充值订单数据 订单的完成时间 状态
                CashWithdrawals cashWithdrawalsx = getById(cashWithdrawals);
                if (cashWithdrawalsx == null) {
                    throw new IllegalArgumentException("订单数据错误");
                }
                cashWithdrawalsx.setLastTime(new Date());
                cashWithdrawalsx.setStatus(cashWithdrawals.getStatus());
                cashWithdrawalsx.setRemark(cashWithdrawals.getRemark());
                updateById(cashWithdrawalsx);

                //给该用户账户增加
                QueryWrapper<Coin> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("name", "CNY");
                List<Coin> list = coinService.list(queryWrapper);
                if (CollectionUtils.isEmpty(list)) {
                    throw new IllegalArgumentException("CNY未配置");
                }

                //添加资金流水
                accountService.transfer(cashWithdrawalsx.getCoinId(),list.get(0).getId(),
                        cashWithdrawalsx.getUserId(), null, cashWithdrawalsx.getId(),
                        AccountEnums.withdrawals_out.getCode(), cashWithdrawalsx.getMum(),
                        cashWithdrawalsx.getFee(), cashWithdrawalsx.getRemark());

                //提现的流水
                CashWithdrawAuditRecord cashWithdrawAuditRecord = new CashWithdrawAuditRecord();
                Long userId = UserUtil.getUser();
                cashWithdrawAuditRecord.setAuditUserId(userId);
                cashWithdrawAuditRecord.setCreated(new Date());
                cashWithdrawAuditRecord.setOrderId(cashWithdrawalsx.getId());
                cashWithdrawAuditRecord.setRemark(cashWithdrawals.getRemark());
                cashWithdrawAuditRecord.setStatus((byte)1);
                cashWithdrawAuditRecord.setId(snowflake.nextId());

                //远程调用查询管理员的名称
                Map<Long, AdminUserDto> adminUser = adminUserFegin.getAdminUser(Arrays.asList(userId));
                AdminUserDto adminUserDto = adminUser.get(userId);
                cashWithdrawAuditRecord.setAuditUserName(adminUserDto.getUsername());

                cashWithdrawAuditRecordService.save(cashWithdrawAuditRecord);


                } else {
                //审核 失败的情况

                CashWithdrawals cashWithdrawalsx = getById(cashWithdrawals);
                if (cashWithdrawalsx == null) {
                    throw new IllegalArgumentException("订单数据错误");
                }
                cashWithdrawalsx.setLastTime(new Date());
                cashWithdrawalsx.setStatus(cashWithdrawals.getStatus());
                cashWithdrawalsx.setRemark(cashWithdrawals.getRemark());
                updateById(cashWithdrawalsx);


                //提现的流水
                CashWithdrawAuditRecord cashWithdrawAuditRecord = new CashWithdrawAuditRecord();
                Long userId = UserUtil.getUser();
                cashWithdrawAuditRecord.setAuditUserId(userId);
                cashWithdrawAuditRecord.setCreated(new Date());
                cashWithdrawAuditRecord.setOrderId(cashWithdrawalsx.getId());
                cashWithdrawAuditRecord.setRemark(cashWithdrawals.getRemark());
                cashWithdrawAuditRecord.setStatus((byte)1);
                cashWithdrawAuditRecord.setId(snowflake.nextId());

                //远程调用查询管理员的名称
                Map<Long, AdminUserDto> adminUser = adminUserFegin.getAdminUser(Arrays.asList(userId));
                AdminUserDto adminUserDto = adminUser.get(userId);
                cashWithdrawAuditRecord.setAuditUserName(adminUserDto.getUsername());

                cashWithdrawAuditRecordService.save(cashWithdrawAuditRecord);

                //把冻结余额中的资金 -》 可用账户
                accountToLock(cashWithdrawalsx.getNum(),cashWithdrawalsx.getCoinId(),cashWithdrawalsx.getUserId(),(byte)DirectionEnums.IN.getCode());
            }
        });

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sell(CashWithdrawals cashWithdrawals) {

        //判断手机验证码
        Map<Long, UserDto> user = userFegin.getUser(Arrays.asList(UserUtil.getUser()), null, null, null, null);
        UserDto userDto = user.get(UserUtil.getUser());
        String mobile = userDto.getMobile();
        String newV = (String) redisTemplate.opsForValue().get("SMS:CASH_WITHDRAWS:" + mobile);

        if (StringUtils.isEmpty(newV)) {
            throw new IllegalArgumentException("验证码过期");
        }
        if (!newV.equals(cashWithdrawals.getValidateCode())) {
            throw new IllegalArgumentException("验证码错误");
        }
        //判断交易密码
        String paypassword = userDto.getPaypassword();
        if(StringUtils.isEmpty(paypassword)){
            throw new IllegalArgumentException("当前用户未设置交易密码，请先设置交易密码");
        }
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        boolean matches = bCryptPasswordEncoder.matches(cashWithdrawals.getPayPassword(), paypassword);
        if (!matches) {
            throw new IllegalArgumentException("用户的交易密码输入错误");
        }
        //添加一条GCN的提现审核记录

        cashWithdrawals.setUserId(UserUtil.getUser());
        cashWithdrawals.setStatus((byte)0);
        Account userAccountByCoinName = accountService.findUserAccountByCoinName(cashWithdrawals.getCoinId(), null,UserUtil.getUser());
        cashWithdrawals.setAccountId(userAccountByCoinName.getId());
        BigDecimal sellRate = userAccountByCoinName.getSellRate();
        cashWithdrawals.setMum(sellRate.multiply(cashWithdrawals.getNum()));
        //手续费 = 用户提现数量 - 用户实际到账数量
        cashWithdrawals.setFee(cashWithdrawals.getNum().subtract(cashWithdrawals.getMum()));

        //设置用户银行卡信息
        UserBankDto userBank = userBankFegin.getUserBank(Arrays.asList(UserUtil.getUser()));
        cashWithdrawals.setTruename(userBank.getRealName());
        cashWithdrawals.setBank(userBank.getBank());
        cashWithdrawals.setBankCard(userBank.getBankCard());
        cashWithdrawals.setBankCity(userBank.getBankCity());
        cashWithdrawals.setBankProv(userBank.getBankProv());
        cashWithdrawals.setBankAddr(userBank.getBankAddr());

        cashWithdrawals.setRemark("用户提现");
        cashWithdrawals.setStep((byte)1);
        cashWithdrawals.setCreated(new Date());
        cashWithdrawals.setLastUpdateTime(new Date());
        //保存提现订单数据
        save(cashWithdrawals);

        //把当前的可用余额转到冻结余额
        accountToLock(cashWithdrawals.getNum(),cashWithdrawals.getCoinId(),UserUtil.getUser(), (byte)DirectionEnums.OUT.getCode());
    }

    @Override
    public void accountToLock(BigDecimal amount, Long coinId,Long userId,Byte direction) {
        //把冻结 -》 可用
        Account account = accountService.findUserAccountByCoinName(coinId, null,userId);
        if(direction == DirectionEnums.IN.getCode()){
            //判断用户的余额情况
            //修改余额
            if((account.getFreezeAmount().subtract(amount)).compareTo(BigDecimal.ZERO)<0){
                throw new IllegalArgumentException("用户的冻结余额不足");
            }
            account.setFreezeAmount(account.getFreezeAmount().subtract(amount));
            account.setBalanceAmount(account.getBalanceAmount().add(amount));
            accountService.updateById(account);
        }
        //把可用 -》 冻结
        else {
            if((account.getBalanceAmount().subtract(amount)).compareTo(BigDecimal.ZERO)<0){
                throw new IllegalArgumentException("用户的余额不足");
            }
            account.setFreezeAmount(account.getFreezeAmount().add(amount));
            account.setBalanceAmount(account.getBalanceAmount().subtract(amount));
            accountService.updateById(account);
        }
    }



}
